<?php

declare(strict_types=1);
/**
 * This file is part of habo_new_media.
 *
 * @link     https://www.berlen.cn/
 * @contact  tech@berlen.cn
 * @license  http://gitlab.rehou.cn/habo/habo_new_media/-/blob/master/README.md
 */
namespace app\service\finance\statement;

use app\enum\AccountEnum;
use app\enum\CommonEnum;
use app\model\AccountApplyDetailModel;
use app\model\AccountPortModel;
use app\model\AccountTransferModel;
use app\model\AccountTransferProviderModel;
use app\model\AccountTransferTypeModel;
use app\model\ClientAccountModel;
use app\model\CompanyTeamModel;
use app\model\mysql\AccountStatementStatModel;
use app\model\mysql\ClientSignCompanyModel;
use app\service\client\AccountService;
use app\service\client\ClientService;
use app\service\client\ProductService;
use app\service\finance\AccountRangeService;
use app\service\finance\cost\CostAccountService;
use app\service\finance\cost\CostService;
use app\service\finance\DeductionService;
use app\service\finance\FinanceService;
use app\service\finance\ProviderService;
use app\service\staff\StaffService;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;

class StatementService extends FinanceService implements StatementInterface
{
    /**
     * @var int 月份
     */
    public int $statMonth;

    /**
     * @var int 月份第一天
     */
    public int $startDate;

    /**
     * @var int 月份第最后
     */
    public int $endDate;

    /**
     * @var null|ClientAccountModel 账户模型
     */
    private ?ClientAccountModel $clientAccountModel;

    /**
     * @var array 类型时段
     */
    private array $typeRange = [];

    /**
     * @var array 端口时段
     */
    private array $portRange = [];

    /**
     * @var array 服务时段
     */
    private array $serviceRange = [];

    /**
     * @var array 时段
     */
    private array $range = [];

    /**
     * @var array 默认数据
     */
    private array $defaultData = [];

    private ?CostAccountService $costService;

    public function __construct(ClientAccountModel $clientAccountModel, int $month = 0)
    {
        $this->clientAccountModel = $clientAccountModel;
        $this->statMonth = $month;
        $this->startDate = intval($month . '01');
        $this->endDate = intval(date('Ymd', strtotime($this->startDate . '+1 month -1 day')));
        $this->costService = CostService::getInstance(intval($clientAccountModel->getAttr('media_id')));
    }

    /**
     * 获取消耗数据.
     * @param int $startDate 开始日期
     * @param int $endDate 结束日期
     * @return mixed
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getCost(int $startDate = 0, int $endDate = 0)
    {
        return $this->costService::getCost($this->clientAccountModel->getAttr('advertiser_id'), $startDate ?: $this->startDate, $endDate ?: $this->endDate);
    }

    /**
     * 获取类型时段.
     */
    public function getTypeRange(): array
    {
        return $this->typeRange;
    }

    /**
     * 获取端口时段.
     */
    public function getPortRange(): array
    {
        return $this->portRange;
    }

    /**
     * 获取服务类型时段.
     */
    public function getServiceRange(): array
    {
        return $this->serviceRange;
    }

    /**
     * 设置端口.
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function setPort()
    {
        $where = [
            ['advertiser_id', '=', $this->clientAccountModel->getAttr('advertiser_id')],
            ['set_time', '>=', date('Y-m-d', strtotime(strval($this->startDate)))],
            ['set_time', '<=', date('Y-m-d', strtotime(strval($this->endDate)) + 86400)],
        ];
        $ports = (new AccountTransferModel())->where($where)->order('set_time desc')
            ->field('start_date,end_date,set_time,now_port_id,old_port_id')
            ->select()->toArray();
        if (empty($ports)) {
            $portId = (new AccountPortModel())->where('advertiser_id', $this->clientAccountModel->getAttr('advertiser_id'))->value('port_id');
            $data = [
                ['port_id' => $portId, 'start_date' => $this->startDate, 'end_date' => $this->endDate],
            ];
        } else {
            foreach ($ports as $k => $v) {
                $startDate = date('Ymd', strtotime($v['start_date']));
                $endDate = date('Ymd', strtotime($v['end_date']));
                $data[] = [
                    'port_id' => $v['old_port_id'],
                    'start_date' => $startDate < $this->startDate ? $this->startDate : $startDate,
                    'end_date' => $endDate > $this->endDate ? $this->endDate : $endDate,
                ];
                if ($k == 0 && $v['set_time'] <= date('Y-m-d', strtotime(strval($this->endDate)))) {
                    $data[] = [
                        'port_id' => $v['now_port_id'],
                        'start_date' => $startDate < $v['set_time'] ? $this->startDate : $startDate,
                        'end_date' => $this->endDate,
                    ];
                }
            }
        }
        if (empty($data)) {
            return self::setError('端口设置失败');
        }
        $this->portRange = $data;
        return true;
    }

    /**
     * 设置账户类型.
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function setAccountType(): bool
    {
        $where = [
            ['advertiser_id', '=', $this->clientAccountModel->getAttr('advertiser_id')],
            ['set_time', '>=', date('Y-m-d', strtotime(strval($this->startDate)))],
            ['set_time', '<=', date('Y-m-d', strtotime(strval($this->endDate)) + 86400)],
        ];
        $types = (new AccountTransferTypeModel())->where($where)->order('set_time desc')->select()->toArray();
        if (empty($types)) {
            $data = [
                ['account_type' => $this->clientAccountModel->getAttr('account_type'), 'start_date' => $this->startDate, 'end_date' => $this->endDate],
            ];
        } else {
            foreach ($types as $k => $v) {
                $startDate = date('Ymd', strtotime($v['start_date']));
                $endDate = date('Ymd', strtotime($v['end_date']));
                $data[] = [
                    'account_type' => $v['old_account_type'],
                    'start_date' => $startDate < $this->startDate ? $this->startDate : $startDate,
                    'end_date' => $endDate > $this->endDate ? $this->endDate : $endDate,
                ];
                if ($k == 0 && $v['set_time'] <= date('Y-m-d', strtotime(strval($this->endDate)))) {
                    $data[] = [
                        'account_type' => $v['now_account_type'],
                        'start_date' => $startDate < $v['set_time'] ? $this->startDate : $startDate,
                        'end_date' => $this->endDate,
                    ];
                }
            }
        }
        if (empty($data)) {
            return self::setError('账户类型设置失败');
        }
        $this->typeRange = $data;
        return true;
    }

    /**
     * 设置供应商服务
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function setProviderService(): bool
    {
        $where = [
            ['account_id', '=', $this->clientAccountModel->getKey()],
        ];
        $services = (new AccountTransferProviderModel())->where($where)->order('id desc')->select()->toArray();
        if (empty($services)) {
            // 查询政策时间
            $provider = (new AccountApplyDetailModel())->where('advertiser_id', $this->clientAccountModel->getAttr('advertiser_id'))->find();
            $data = [
                ['service_id' => $provider->getAttr('provider_service_id'), 'service_type' => $provider->getAttr('provider_service_type'), 'provider_id' => $provider->getAttr('provider_id'), 'start_date' => $this->startDate, 'end_date' => $this->endDate],
            ];
        } else {
            foreach ($services as $k => $v) {
                if ($k == 0) {
                    // 首次
                    $data[] = [
                        'provider_id' => $v['old_provider_id'],
                        'service_id' => $v['old_service_id'],
                        'service_type' => $v['old_service_type'],
                        'start_date' => max($v['old_start_date'], $this->startDate),
                        'end_date' => min($v['old_end_date'], $this->endDate),
                    ];
                }
                $data[] = [
                    'provider_id' => $v['new_provider_id'],
                    'service_id' => $v['new_service_id'],
                    'service_type' => $v['new_service_type'],
                    'start_date' => max($v['new_start_date'], $this->startDate),
                    'end_date' => min($v['new_end_date'], $this->endDate),
                ];
            }
        }
        if (empty($data)) {
            return self::setError('服务类型设置失败');
        }
        $this->serviceRange = $data;
        return true;
    }

    /**
     * 初始化账户属性.
     * @return true
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function initStat(): bool
    {
        $costData = $this->getCost($this->startDate, $this->endDate);
        if ($costData['cost'] == 0) {
            return self::setError('账户无消耗');
        }
        if (! $this->setAccountType() || ! $this->setPort()) {
            return false;
        }
        if ($this->clientAccountModel->getAttr('account_type') == AccountEnum::ACCOUNT_TYPE_PROVIDER) {
            if (! $this->setProviderService()) {
                return false;
            }
        }
        if (! $this->range = (new AccountRangeService($this))->beRange()) {
            return self::setError('生成账户信息失败');
        }

        return $this->setDefaultData();
    }

    /**
     * 保存数据.
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function saveStat(): bool
    {
        foreach ($this->range as $val) {
            $cost = 0;
            $cashCost = 0;
            $rewardCost = 0;
            foreach ($val['date_range'] as $item) {
                $costData = $this->getCost($item['start_date'], $item['end_date']);
                if ($costData) {
                    $cost += $costData['cost'];
                    $cashCost += $costData['cash_cost'];
                    $rewardCost += $costData['reward_cost'];
                }
            }
            if (! $cost) {
                continue;
            }
            $model = new AccountStatementStatModel();
            $data = $this->defaultData;
            $data['port_id'] = $val['port_id'];
            $data['port_name'] = AccountService::getPortName($val['port_id']);
            $data['account_type'] = $val['account_type'];
            $data['account_type_name'] = AccountEnum::ACCOUNT_TYPE_NAME[$val['account_type']] ?? '';
            $data['cost'] = $cost;
            $data['cash_cost'] = $cashCost;
            $data['reward_cost'] = $rewardCost;
            if ($val['account_type'] == AccountEnum::ACCOUNT_TYPE_PROVIDER) {
                $serviceRate = ProviderService::getProviderService(intval($val['provider_service_type']), intval($val['provider_service_id']));
                $data['provider_name'] = ProviderService::getProviderName(intval($val['provider_id']));
                $data['provider_service'] = $serviceRate;
                $data['provider_service_id'] = $val['provider_service_id'];
                $data['provider_service_type'] = $val['provider_service_type'];
                $data['provider_amount'] = $cashCost * ($serviceRate / 100);
            }
            if ($val['account_type'] == AccountEnum::ACCOUNT_TYPE_SELF) {
                // 运营数据
                $operation = AccountService::getOperation($this->clientAccountModel->getAttr('advertiser_id'));
                // 运营
                if ($operation) {
                    $this->defaultData['operation_team'] = (new CompanyTeamModel())->where('id', $operation['team_id'])->value('team_name') ?: '';
                    $this->defaultData['operation_name'] = StaffService::getStaffName(intval($operation['staff_id'])) ?: '';
                }
            }
            // 自收走
            if ($this->clientAccountModel->getAttr('media_id') != CommonEnum::OE_MEDIA) {
                $data['account_info'] = AccountEnum::ACCOUNT_TYPE_STATEMENT[$val['account_type']] ?? '';
            }
            $model->setAttrs(
                $data
            );
            $model->save();
        }
        return true;
    }

    /**
     * 设置默认字段属性值.
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private function setDefaultData(): bool
    {
        // 开户数据
        $applyData = AccountService::getAccountApplyInfo($this->clientAccountModel->getAttr('advertiser_id'));
        if (! $applyData) {
            return self::setError('获取账户开户信息失败');
        }
        // 客户数据
        $clientData = ClientService::getClient($this->clientAccountModel->getAttr('client_id'));
        if (! $clientData) {
            return self::setError('获取客户信息失败');
        }
        $accountInfo = AccountService::getAccountIndustry($this->clientAccountModel->getAttr('advertiser_id'), $this->clientAccountModel->getAttr('media_id'));
        $this->defaultData = [
            'stat_month' => $this->statMonth,
            'account_id' => $this->clientAccountModel->getKey(),
            'advertiser_id' => $this->clientAccountModel->getAttr('advertiser_id'),
            'client_id' => $this->clientAccountModel->getAttr('client_id'),
            'product_id' => $applyData['product_id'] ?? 0,
            'media_id' => $this->clientAccountModel->getAttr('media_id'),
            'industry_one' => $accountInfo['industry_one'] ?? '',
            'industry_two' => $accountInfo['industry_two'] ?? '',
            'product_name' => ProductService::getProductName(intval($applyData['product_id'])),
            'client_name' => $clientData['client_short_name'],
            'sign_company' => (new ClientSignCompanyModel())->where('client_id', $this->clientAccountModel->getAttr('client_id'))->value('sign_company_full_name'),
            'sale_name' => StaffService::getStaffName(intval($clientData['create_staff_id'])),
            'deduction_amount' => DeductionService::getDeductionAmount($this->clientAccountModel->getKey(), $this->statMonth),
        ];
        return true;
    }
}
